package org.javanum.number;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;

/**
 * User: Scott Fines
 * Date: Dec 11, 2009
 * Time: 10:47:16 PM
 */
@RunWith(Parameterized.class)
public class RealTest {
    private final Real num1;
    private final Real num2;
    private static final double DELTA = Math.pow(10,-10);

    public RealTest(Real num1,Real num2){
        this.num1=num1;
        this.num2=num2;
    }

    @Parameterized.Parameters
    public static Collection<Object[]> getParameters(){
        Random rand = new Random();
        int randomDataValue = 100;
        Collection<Object[]> data =
                new ArrayList<Object[]>(randomDataValue);
        for(int i=1;i<randomDataValue;i++){
            int r1 = rand.nextInt();
            double rand1 = rand.nextDouble()*r1;

            int r2 = rand.nextInt();
            double rand2 = rand.nextDouble()*r2;
            data.add(new Object[]{new Real(rand1),new Real(rand2)});
        }
        return data;
    }

    @Test
    public void testAbs() throws Exception {
        Real r = getOneNumber();
        assumeNotNull(r);

        Real abs = r.abs();

        assertTrue("Absolute value is negative",abs.value>=0);
        assertTrue("Absolute value has unequal magnitude",
                abs.value==Math.abs(r.value));

    }

    private Real getOneNumber() {
        if(num1==null)
            return num2;
        else
            return num1;
    }

    @Test
    public void testAdd() throws Exception {
        Real firstNum = num1;
        assumeNotNull(firstNum);
        Real secondNum = num2;
        assumeNotNull(num2);

        Real real = firstNum.add(secondNum);
        double result = real.value;
        double testResult = firstNum.value+
                secondNum.value;
        assertEquals("real values do not match",result,testResult,DELTA);
    }

    @Test
    public void testDivideNonZero() throws Exception {
        Real firstNum=num1;
        Real secondNum=num2;
        assumeNotNull(firstNum);
        assumeNotNull(secondNum);
        assumeTrue(secondNum.value!=0);

        Real real = firstNum.divide(secondNum);
        double result = real.value;
        double testResult =
                firstNum.value/secondNum.value;

        assertEquals("real values do not match",result,testResult,DELTA);
    }

    @SuppressWarnings({"UnusedDeclaration"})
    @Test(expected=ArithmeticException.class)
    public void testDivideByZero() throws Exception{
        Real firstNum = getOneNumber();
        assumeNotNull(firstNum);

        Real ZERO = Real.ZERO;
        Real real = firstNum.divide(ZERO);

    }

    @SuppressWarnings({"ObjectEqualsNull"})
    @Test
    public void testEquals() throws Exception {
        Real firstNum = getOneNumber();
        assumeNotNull(firstNum);
        assertFalse("Nullity fails: firstNum.equals(null)=true",
                        firstNum.equals(null));
        assertTrue("Reflexivity fails: x!=itself",firstNum.equals(firstNum));

        Real x = new Real(firstNum);
        assertTrue("Reflexivity fails: x!=firstNum",x.equals(firstNum));
        assertTrue("Symmetry fails: x.equals(firstNum)=true," +
                    "but firstNum.equals(x)=false",
                firstNum.equals(x));

        Real y = new Real(x);
        assertTrue("Transitivity fails: x!=y",x.equals(y));
        assertTrue("Transitivity fails: firstNum.equals(x) and x.equals(y)," +
                    "but firstNum.equals(y)=false",firstNum.equals(y));

    }

    @Test
    public void testNonEquals() throws Exception{
        Real firstNum = num1;
        Real secondNum = num2;
        assumeNotNull(firstNum);
        assumeNotNull(secondNum);
        assumeTrue(firstNum.value!=secondNum.value);

        assertFalse("Non equal elements evaluate equals() to true",
                    firstNum.equals(secondNum));
    }

    @Test
    public void testHashCode() throws Exception {
        Real firstNum = num1;
        assumeNotNull(firstNum);

        Real secondNum = new Real(firstNum);
        assumeTrue(secondNum.equals(firstNum));

        assertEquals("Equal elements hash to different values",
                    firstNum.hashCode(),secondNum.hashCode());

        Real thirdNum = num2;
        assumeNotNull(thirdNum);
        assumeTrue(!thirdNum.equals(firstNum));

        assertTrue("Unequal elements hash to the same value",
                        firstNum.hashCode()!=thirdNum.hashCode());

    }

    @Test
    public void testMultiply() throws Exception {
        Real firstNum = num1;
        Real secondNum = num2;

        assumeNotNull(firstNum);
        assumeNotNull(secondNum);
        assumeTrue(firstNum.value!=secondNum.value);

        Real real = num1.multiply(num2);
        double result = real.value;
        double testResult =
                firstNum.value*secondNum.value;

        assertEquals("result !=testResult",result,testResult,DELTA);
    }

    @Test
    public void testSubtract() throws Exception {
        Real firstNum = num1;
        Real secondNum=num2;

        assumeNotNull(firstNum);
        assumeNotNull(secondNum);

        Real real = firstNum.subtract(secondNum);
        double result = real.value;
        double testResult =
                firstNum.value-secondNum.value;

        assertEquals("result!=testResult",result,testResult,DELTA);

    }

    @Test
    public void testAdditiveInverse() throws Exception {
        Real x = getOneNumber();

        Real y = x.additiveInverse();

        Real result = x.add(y);

        assertEquals("Additive inverse fails.",
                      0.0,result.value,DELTA);

        Real r = y.add(x);
        assertEquals("Addition is not commutative",
                0.0,r.value,DELTA);
    }

    @Test
    public void testMultiplicativeInverse() throws Exception {
        Real x = getOneNumber();

        Real y = x.multiplicativeInverse();

        Real result = x.multiply(y);

        assertEquals("Multiplicative inverse fails: x*y!=1",
                1.0,result.value,DELTA);

        Real r = y.multiply(x);

        assertEquals("Multiplication is not commutative",
                    1.0,r.value,DELTA);
    }
}
